#include "z_logger.h"
#include "asdu_handler.h"

static void single_command_parse(InformationObject io, command_params_t *param) {
    SingleCommand self = (SingleCommand) io;

    param->single_value = SingleCommand_getState(self);
    param->select = SingleCommand_isSelect(self);
    param->qos = SingleCommand_getQU(self);
}

static bool single_command_execute(void *parameter, int tag, command_params_t *param) {
    log_debug("tag=%i, value=%i, select=%d, qos=%d", tag,
              param->single_value, param->select, param->qos);
    return true;
}

static void double_command_parse(InformationObject io, command_params_t *param) {
    DoubleCommand self = (DoubleCommand) io;

    param->double_value = DoubleCommand_getState(self);
    param->select = DoubleCommand_isSelect(self);
    param->qos = DoubleCommand_getQU(self);
}

static bool double_command_execute(void *parameter, int tag, command_params_t *param) {
    log_debug("tag=%i, value=%i, select=%d, qos=%d", tag,
              param->double_value, param->select, param->qos);
    return true;
}

static void step_command_parse(InformationObject io, command_params_t *param) {
    StepCommand self = (StepCommand) io;

    param->step_value = StepCommand_getState(self);
    param->select = StepCommand_isSelect(self);
    param->qos = StepCommand_getQU(self);
}

static bool step_command_execute(void *parameter, int tag, command_params_t *param) {
    log_debug("tag=%i, value=%i, select=%d, qos=%d", tag,
              param->step_value, param->select, param->qos);
    return true;
}

static void set_normalized_parse(InformationObject io, command_params_t *param) {
    SetpointCommandNormalized self = (SetpointCommandNormalized) io;

    param->normalized_value = SetpointCommandNormalized_getValue(self);
    param->select = SetpointCommandNormalized_isSelect(self);
    param->qos = SetpointCommandNormalized_getQL(self);
}

static bool set_normalized_execute(void *parameter, int tag, command_params_t *param) {
    log_debug("tag=%i, value=%f, select=%d, qos=%d", tag,
              param->normalized_value, param->select, param->qos);
    return true;
}

static void set_scaled_parse(InformationObject io, command_params_t *param) {
    SetpointCommandScaled self = (SetpointCommandScaled) io;

    param->scaled_value = SetpointCommandScaled_getValue(self);
    param->select = SetpointCommandScaled_isSelect(self);
    param->qos = SetpointCommandScaled_getQL(self);
}

static bool set_scaled_execute(void *parameter, int tag, command_params_t *param) {
    log_debug("tag=%i, value=%i, select=%d, qos=%d", tag,
              param->scaled_value, param->select, param->qos);
    return true;
}

static void set_float_parse(InformationObject io, command_params_t *param) {
    SetpointCommandShort self = (SetpointCommandShort) io;

    param->float_value = SetpointCommandShort_getValue(self);
    param->select = SetpointCommandShort_isSelect(self);
    param->qos = SetpointCommandShort_getQL(self);
}

static bool set_float_execute(void *parameter, int tag, command_params_t *param) {
    log_debug("tag=%i, value=%f, select=%d, qos=%d", tag,
              param->float_value, param->select, param->qos);
    return true;
}

static const command_t commands[] = {
        {C_SC_NA_1, single_command_parse, single_command_execute},
        {C_DC_NA_1, double_command_parse, double_command_execute},
        {C_RC_NA_1, step_command_parse,   step_command_execute},
        {C_SE_NA_1, set_normalized_parse, set_normalized_execute},
        {C_SE_NB_1, set_scaled_parse,     set_scaled_execute},
        {C_SE_NC_1, set_float_parse,      set_float_execute},
};

bool asdu_handler(void *parameter, IMasterConnection connection, CS101_ASDU asdu) {
    for (int i = 0; i < sizeof(commands) / sizeof(commands[0]); ++i) {
        if (CS101_ASDU_getTypeID(asdu) == commands[i].type_id) {
            if (CS101_ASDU_getCOT(asdu) == CS101_COT_ACTIVATION) {
                InformationObject io = CS101_ASDU_getElement(asdu, 0);

                command_params_t param;
                commands[i].on_parse(io, &param);

                int tag = InformationObject_getObjectAddress(io);
                if (commands[i].on_execute(parameter, tag, &param)) {
                    CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON);
                } else {
                    CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_IOA);
                }

                InformationObject_destroy(io);
            } else {
                CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_COT);
            }

            IMasterConnection_sendASDU(connection, asdu);

            return true;
        }
    }

    return false;
}